package uns.fit.gis.mapio;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Vector;

import uns.fit.gis.mapobj.Crossroads;
import uns.fit.gis.mapobj.Path;
import uns.fit.gis.mapobj.Point;
import uns.fit.gis.mapobj.RealPoint;
import uns.fit.gis.mapobj.Rectangle;
import uns.fit.gis.mapobj.Region;
import uns.fit.gis.mapobj.Segment;
import uns.fit.gis.mapobj.SimpleLocation;
import uns.fit.gis.Util;

public class MapReader {
	// Segments of all paths
	//private final int NUMBER_SEGMENTS = 3428;
	//private final int NUMBER_CROSSROADSES = 5274;
	private final int NUMBER_SEGMENTS = 7713;
	public Segment[] segments;
	public Crossroads[] cross;
	public Path[] paths;
	public Region[] regions;
	public SimpleLocation[] locations;
	/*
	 * 
	 */
	public Path[] readPathsFromBinaryFile(String fileName) throws IOException {
		DataInputStream in = new DataInputStream(this.getClass()
				.getResourceAsStream(fileName));
		int n = in.readInt();
		segments = new Segment[NUMBER_SEGMENTS];
		Path[] paths = new Path[n];
		int segmentIndex = 0;
		int id = 0;
		try {
			while (n > 0) {
				Path path = new Path();
				//int id = in.readShort() & 0xFFFF;
				//path.setId(id);
				
				int numSegments = in.readByte() & 0xFF;
				int[] segmentIndices = new int[numSegments];
				for (int i = 0; i < numSegments; i++) {
					Segment segment = new Segment();
					//segment.setId(in.readShort() & 0xFFFF);
					segment.setId(segmentIndex);
					segment.setPathId(id);
					segment.setType(in.readByte());
					segment.setType2(in.readByte());
					
					segment.setLength(in.readInt());
					segment.setCross0(in.readShort());
					segment.setCross1(in.readShort());
					
					int numPoints = in.readByte() & 0xFF;
					segment.setNumberOfPoints(numPoints);
					
					for(int j = 0; j < numPoints; j++) {
						short x = in.readShort();
						short y = in.readShort();

						int _x = x & 0xFFFF;
						int _y = y & 0xFFFF;
						if (segment.getMaxX() < _x)
							segment.setMaxX(_x);
						if (segment.getMaxY() < _y)
							segment.setMaxY(_y);
						if (segment.getMinX() > _x)
							segment.setMinX(_x);
						if (segment.getMinY() > _y)
							segment.setMinY(_y);

						segment.setX(j, x);
						segment.setY(j, y);
					}
					
					/*
					 * Just for funny test
					 */
					//if(segmentIndex == 3322 || segmentIndex == 3321)
					//	segment.reverseAllPoints();
					/*
					 * End of funny test
					 */
					
					segments[segmentIndex] = segment;
					segmentIndex++;
					segmentIndices[i] = segment.getId();
				}
				int numBytes = in.readByte() & 0xFF;
				if (numBytes > 0) {
					byte[] name = new byte[numBytes];
					for (int i = 0; i < numBytes; i++) {
						name[i] = in.readByte();
					}
					path.setName(name);
				}
				
				n--;
				path.setSegments(segmentIndices);
				//paths[path.getId()] = path;
				paths[id] = path;
				path = null;
				id ++;
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			in.close();
		}
		return paths;
	}
	
	/*
	 * 
	 */
	public Region[] readPolygonsFromBinaryFile(String fileName) throws IOException{
		DataInputStream in = new DataInputStream(getClass()
				.getResourceAsStream(fileName));
		int n = in.readInt();
		Region[] regions = new Region[n];
		int id = 0;
		try {
			while (n > 0) {
				Region region = new Region();
				
				//int id = in.readShort() & 0xFFFF;
				region.setId(id);
				
				region.setType(in.readByte());
				
				/*
				 * Temporarily not remove
				 * Only remove when having correct map data by removing MBR
				 */
				/*
				int maxX = in.readShort() & 0xFFFF;
				int maxY = in.readShort() & 0xFFFF;
				int minX = in.readShort() & 0xFFFF;
				int minY = in.readShort() & 0xFFFF;
				
				region.setMaxX(maxX);
				region.setMaxY(maxY);
				region.setMinX(minX);
				region.setMinY(minY);
				*/
				
				int numPoints = in.readByte() & 0xFF;
				region.setNumberOfPoints(numPoints);
				//Point[] points = new Point[numPoints];
				for (int i = 0; i < numPoints; i++) {
					int x = in.readShort() & 0xFFFF;
					int y = in.readShort() & 0xFFFF;
					
					if (region.getMaxX() < x)
						region.setMaxX(x);
					if (region.getMaxY() < y)
						region.setMaxY(y);
					if (region.getMinX() > x)
						region.setMinX(x);
					if (region.getMinY() > y)
						region.setMinY(y);
					
					region.setX(i, x);
					region.setY(i, y);
				}
				
				int nameLength = in.readByte() & 0xFF;
				if (nameLength > 0) {
					byte[] name = new byte[nameLength];
					for (int i = 0; i < nameLength; i++)
						name[i] = in.readByte();
					region.setName(name);
				}
				n--;
				regions[id] = region;
				id ++;
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			in.close();
		}
		
		System.out.println("FIRST REGION " + regions[regions.length - 1].getId());
		return regions;
	}
	
	/*
	 * 
	 */
	public SimpleLocation[] readPOIsFromBinaryFile(String fileName) throws IOException{
		DataInputStream in = new DataInputStream(this.getClass()
				.getResourceAsStream(fileName));
		int n = in.readInt();
		SimpleLocation[] locations = new SimpleLocation[n];
		int id = 0;
		try {
			while (n > 0) {
				SimpleLocation location = new SimpleLocation();
				//location.setId(in.readShort() & 0xFFFF);
				location.setId(id);
				location.setType(in.readByte());
				
				int x = in.readShort() & 0xFFFF;
				int y = in.readShort() & 0xFFFF;
				//location.setPoint(new Point(x, y));
				location.setX(x);
				location.setY(y);
				
				int nameLength = in.readByte() & 0xFF;
				if(nameLength > 0) {
					byte[] name = new byte[nameLength];
					for (int i = 0; i < nameLength; i++)
						name[i] = in.readByte();
					location.setName(name);
				}
				
				n--;
				locations[id] = location;
				id ++;
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			in.close();
		}
		return locations;
	}
	
	/*
	 * 
	 */
	public Crossroads[] readCrossroadsesFromBinaryFile(String fileName) throws IOException{
		DataInputStream in = new DataInputStream(this.getClass()
				.getResourceAsStream(fileName));
		int n = in.readInt();
		
		Crossroads[] cross = new Crossroads[n];
		int id = 0;
		try {
			while (n > 0) {
				Crossroads crossroads = new Crossroads();
				//int id = in.readShort() & 0xFFFF;
				crossroads.setId(id);
				
				int x = in.readShort() & 0xFFFF;
				int y = in.readShort() & 0xFFFF;
				crossroads.setX(x);
				crossroads.setY(y);
				
				int numSegments = in.readByte() & 0xFF;
				/*
				 * Segment --> Target (another crossroads): Distance + Time
				 */
				int[] segments = new int[numSegments];
				/*
				int[] targets = new int[numSegments];
				int[] distances = new int[numSegments];
				int[] times = new int[numSegments];
				*/
				for(int i = 0; i < numSegments; i++) {
					segments[i] = in.readShort() & 0xFFFF;
					
					//targets[i] = in.readShort() & 0xFFFF;
					//distances[i] = in.readShort() & 0xFFFF;
					//this.segments[segments[i]].setLength(distances[i]);
					//times[i] = in.readShort() & 0xFFFF;
				}
				n--;
				crossroads.setSegments(segments);
				/*
				crossroads.setTargets(targets);
				crossroads.setDistances(distances);
				crossroads.setTimes(times);
				*/
				cross[id] = crossroads;
				id ++;
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			in.close();
		}
		return cross;
	}
	
	/*
	 * Read all map in one file
	 * DOC SAI, xem lai cau truc
	 */
	public void read(String fileName) throws IOException {
		DataInputStream in = new DataInputStream(this.getClass()
				.getResourceAsStream(fileName));
		int n = in.readInt();
		segments = new Segment[NUMBER_SEGMENTS];
		Path[] paths = new Path[n];
		int segmentIndex = 0;
		while (n > 0) {
			Path path = new Path();
			int id = in.readShort() & 0xFFFF;
			int numSegments = in.readByte() & 0xFF;
			int[] segmentIndices = new int[numSegments];
			for (int i = 0; i < numSegments; i++) {
				Segment segment = new Segment();
				segment.setId(in.readShort() & 0xFFFF);
				segment.setPathId(id);
				segment.setType(in.readByte());
				segment.setLevel(in.readByte());
				segment.setLength(in.readInt());
				segment.setCross0(in.readShort());
				segment.setCross1(in.readShort());
				int numPoints = in.readByte() & 0xFF;
				segment.setNumberOfPoints(numPoints);
				for (int j = 0; j < numPoints; j++) {
					short x = in.readShort();
					short y = in.readShort();
					int _x = x & 0xFFFF;
					int _y = y & 0xFFFF;
					if (segment.getMaxX() < _x)
						segment.setMaxX(_x);
					if (segment.getMaxY() < _y)
						segment.setMaxY(_y);
					if (segment.getMinX() > _x)
						segment.setMinX(_x);
					if (segment.getMinY() > _y)
						segment.setMinY(_y);
					segment.setX(j, x);
					segment.setY(j, y);
				}
				segments[segmentIndex] = segment;
				segmentIndex++;
				segmentIndices[i] = segment.getId();
			}
			int numBytes = in.readByte() & 0xFF;
			if (numBytes > 0) {
				byte[] name = new byte[numBytes];
				for (int i = 0; i < numBytes; i++) {
					name[i] = in.readByte();
				}
				path.setName(name);
			}
			n--;
			path.setSegments(segmentIndices);
			paths[id] = path;
			path = null;
		}
		this.paths = paths;

		n = in.readInt();
		Region[] regions = new Region[n];
		while (n > 0) {
			Region region = new Region();
			int id = in.readShort() & 0xFFFF;
			region.setId(id);
			region.setType(in.readByte());
			int numPoints = in.readByte() & 0xFF;
			region.setNumberOfPoints(numPoints);
			for (int i = 0; i < numPoints; i++) {
				int x = in.readShort() & 0xFFFF;
				int y = in.readShort() & 0xFFFF;
				if (region.getMaxX() < x)
					region.setMaxX(x);
				if (region.getMaxY() < y)
					region.setMaxY(y);
				if (region.getMinX() > x)
					region.setMinX(x);
				if (region.getMinY() > y)
					region.setMinY(y);
				region.setX(i, x);
				region.setY(i, y);
			}
			int nameLength = in.readByte() & 0xFF;
			if (nameLength > 0) {
				byte[] name = new byte[nameLength];
				for (int i = 0; i < nameLength; i++)
					name[i] = in.readByte();
				region.setName(name);
			}
			n--;
			regions[n] = region;
		}
		this.regions = regions;

		n = in.readInt();
		SimpleLocation[] locations = new SimpleLocation[n];
		while (n > 0) {
			SimpleLocation location = new SimpleLocation();
			location.setId(in.readShort() & 0xFFFF);
			location.setType(in.readByte());
			int x = in.readShort() & 0xFFFF;
			int y = in.readShort() & 0xFFFF;
			location.setX(x);
			location.setY(y);
			int nameLength = in.readByte() & 0xFF;
			if (nameLength > 0) {
				byte[] name = new byte[nameLength];
				for (int i = 0; i < nameLength; i++)
					name[i] = in.readByte();
				location.setName(name);
			}
			n--;
			locations[n] = location;
		}
		this.locations = locations;

		n = in.readInt();
		Crossroads[] cross = new Crossroads[n];
		while (n > 0) {
			Crossroads crossroads = new Crossroads();
			int id = in.readShort() & 0xFFFF;
			crossroads.setId(id);

			int x = in.readShort() & 0xFFFF;
			int y = in.readShort() & 0xFFFF;
			crossroads.setX(x);
			crossroads.setY(y);

			int numSegments = in.readByte() & 0xFF;
			/*
			 * Segment --> Target (another crossroads): Distance + Time
			 */
			int[] segments = new int[numSegments];

			for (int i = 0; i < numSegments; i++) {
				segments[i] = in.readShort() & 0xFFFF;
			}
			n--;
			crossroads.setSegments(segments);
			cross[crossroads.getId()] = crossroads;
		}
		this.cross = cross;

		in.close();
	}
	
	public Vector readConstructionSites(String fileName) {
		Vector ret = new Vector();
		try {
		DataInputStream in = new DataInputStream(this.getClass()
				.getResourceAsStream(fileName));
		int n = in.readShort() & 0xFFFF;
		for(int i = 0; i < n; i ++)
			ret.addElement(new Integer(in.readShort() & 0xFFFF));
		in.close();
		}
		catch(IOException ioe) {
			ioe.printStackTrace();
		}
		return ret;
	}
}